package repository;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import play.db.jpa.JPA;
import play.libs.F.Function0;
import prefix.duydo.repository.JpaRepository;
import prefix.duydo.repository.Repository;
import prefix.duydo.repository.spec.Specification;
import prefix.duydo.repository.spec.SpecificationBuilder;
import prefix.platform.AbstractPersistent;
import prefix.util.JPAUtil;

public class AbstractRepository extends AbstractPersistent {
    public AbstractRepository() {
    };

    public static <T> T getSingleByProperty(final Class<T> clazz, final String properties, final Object value,
            final LockModeType lockType) {
        SpecificationBuilder builder = SpecificationBuilder.forProperty(properties);
        final Specification<T> sp = builder.equal(value).build();
        Repository repo = new JpaRepository(JPAUtil.em());
        T t = repo.find(clazz, sp, lockType).single();
        return t;

    }

    public static <T> List<T> getListByProperty(final Class<T> clazz, final String properties, final Object LoanId) {
        SpecificationBuilder builder = SpecificationBuilder.forProperty(properties);
        final Specification<T> sp = builder.equal(LoanId).build();
        Repository repo = new JpaRepository(JPAUtil.em());
        List<T> list = repo.find(clazz, sp).list();
        return list;

    }

    public static <T> T getSingleByProperties(final Class<T> clazz, String[] properties, Object[] values) {
        final Specification<T> sp = buildSpec(clazz, properties, values);
        Repository repo = new JpaRepository(JPAUtil.em());
        T model = repo.find(clazz, sp).single();
        return model;
    }

    public static <T> List<T> getListByProperties(final Class<T> clazz, String[] properties, Object[] values) {
        final Specification<T> sp = buildSpec(clazz, properties, values);
        Repository repo = new JpaRepository(JPAUtil.em());
        List<T> list = repo.find(clazz, sp).list();
        return list;
    }

    private static <T> Specification<T> buildSpec(Class<T> clazz, String[] properties, Object[] values) {
        int i = 0;
        Specification<T> sp = null;
        while (i < properties.length) {
            if (i == 0) {
                SpecificationBuilder builder = SpecificationBuilder.forProperty(properties[i]);
                sp = builder.equal(values[i]).build();
            } else {
                sp = andBuilder(clazz, properties[i], values[i], sp);
            }
            i++;
        }
        return sp;
    }

    private static <T> Specification<T> andBuilder(Class<T> clazz, String propertiy, Object value,
            Specification<T> andSpec) {
        SpecificationBuilder builder = SpecificationBuilder.forProperty(propertiy);
        Specification<T> sp = builder.equal(value).build();
        return andSpec.and(sp);
    }

    public static <T extends Enum<T>> String buildInList(T... values) {
        if (values == null || values.length == 0)
            return null;

        StringBuffer sb = new StringBuffer(values.length * 2 + 5);
        sb.append('(').append(values[0].ordinal());
        for (int i = 1; i < values.length; i++) {
            sb.append(',');
            sb.append(values[i].ordinal());
        }
        sb.append(')');
        return sb.toString();
    }

    public static <T extends Enum<T>> Map<Enum<T>, Object> convertResultToMap(List<Object[]> list, T[] values) {
        Map<Enum<T>, Object> map = new HashMap<Enum<T>, Object>();
        for (Object[] objList : list) {
            for (int i = 0; i < objList.length; i++) {
                map.put(values[i], objList[i]);
            }
        }
        return map;
    }

    public static <T extends Enum<T>> Map<Enum<T>, Object> convertResultToMap(Object[] objArray, T[] values) {
        Map<Enum<T>, Object> map = new HashMap<Enum<T>, Object>();
        for (int i = 0; i < objArray.length; i++) {
            map.put(values[i], objArray[i]);
        }
        return map;
    }

}
